﻿using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Seemplest.Core.DataAccess.Attributes;
using Seemplest.Core.DataAccess.DataRecords;
using Seemplest.Core.DataAccess.DataServices;
using Seemplest.Core.DependencyInjection;
using Seemplest.MsSql.DataAccess;
using SoftwareApproach.TestingExtensions;

namespace Seemplest.Core.UnitTests.DataAccess.DataServices
{
    [TestClass]
    public class DataAccessFactoryTest
    {
        private const string DB_CONN = "connStr=Seemplest";

        [ClassInitialize]
        public static void InitializeClass(TestContext context)
        {
            var dc = new SqlDatabase(DB_CONN);
            dc.Execute(
                @"if exists (select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_NAME = 'sample')
                  drop table sample");
            dc.Execute(
                @"create table [dbo].[Sample] (
                    [Id] int not null identity,
                    [Name] nvarchar(64) not null)");
        }

        [TestInitialize]
        public void Initialize()
        {
            ServiceManager.SetRegistry(new DefaultServiceRegistry());
            ServiceManager.Register<ITestDataOperations, TestDataOperations>(DB_CONN);
            DataAccessFactory.SetRegistry(ServiceManager.ServiceRegistry);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void SetRegistryFailsWithNull()
        {
            // --- Act
            DataAccessFactory.SetRegistry(null);
        }

        [TestMethod]
        public void SetRegistryWorksWithDoubleRegistration()
        {
            // --- Arrange
            var registry = new DefaultServiceRegistry();

            // --- Act
            DataAccessFactory.SetRegistry(registry);
            DataAccessFactory.SetRegistry(registry);

            // --- Assert
            registry.ShouldEqual(DataAccessFactory.ServiceRegistry);
        }

        [TestMethod]
        public void CreateContextWorksAsExpected()
        {
            // --- Arrange
            var dc = new SqlDatabase(DB_CONN);
            dc.Execute("delete from [dbo].[Sample]");

            // --- Act
            List<SampleRecord> records;
            using (var ctx = DataAccessFactory.CreateContext<ITestDataOperations>())
            {
                ctx.InsertSample(new SampleRecord {Id = 0, Name = "Haho"});
                ctx.InsertSample(new SampleRecord { Id = 0, Name = "Hello" });
                records = ctx.GetAllSamples();
            }

            // --- Assert
            records.ShouldHaveCountOf(2);
        }

        [TestMethod]
        public void NonGenericCreateContextWorksAsExpected()
        {
            // --- Arrange
            var dc = new SqlDatabase(DB_CONN);
            dc.Execute("delete from [dbo].[Sample]");

            // --- Act
            List<SampleRecord> records;
            using (var ctx = (TestDataOperations)DataAccessFactory.CreateContext(typeof(ITestDataOperations)))
            {
                ctx.InsertSample(new SampleRecord { Id = 0, Name = "Haho" });
                ctx.InsertSample(new SampleRecord { Id = 0, Name = "Hello" });
                records = ctx.GetAllSamples();
            }

            // --- Assert
            records.ShouldHaveCountOf(2);
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void CreateReadOnlyContextWorksAsExpected()
        {
            // --- Arrange
            var dc = new SqlDatabase(DB_CONN);
            dc.Execute("delete from [dbo].[Sample]");

            // --- Act
            using (var ctx = DataAccessFactory.CreateReadOnlyContext<ITestDataOperations>())
            {
                ctx.InsertSample(new SampleRecord { Id = 0, Name = "Haho" });
            }
        }

        [TestMethod]
        public void CreateTrackedContextWorksAsExpected()
        {
            // --- Arrange
            var dc = new SqlDatabase(DB_CONN);
            dc.Execute("delete from [dbo].[Sample]");

            // --- Act
            List<SampleRecord> records;
            using (var ctx = DataAccessFactory.CreateTrackedContext<ITestDataOperations>())
            {
                ctx.InsertSample(new SampleRecord { Id = 0, Name = "Haho" });
                ctx.InsertSample(new SampleRecord { Id = 0, Name = "Hello" });
                records = ctx.GetAllSamples();
            }

            // --- Assert
            records.ShouldHaveCountOf(2);
        }

        [TableName("sample")]
        public class SampleRecord : DataRecord<SampleRecord>
        {
            private int _id;
            private string _name;

            [PrimaryKey]
            [AutoGenerated]
            public int Id
            {
                get { return _id; }
                set { _id = Modify(value, "Id"); }
            }

            public string Name
            {
                get { return _name; }
                set { _name = Modify(value, "Name"); }
            }
        }

        interface ITestDataOperations: IDataAccessOperation
        {
            int InsertSample(SampleRecord record);

            List<SampleRecord> GetAllSamples();
        }

        public class TestDataOperations : SqlDataAccessOperationBase, ITestDataOperations
        {
            public TestDataOperations(string connectionOrName) : base(connectionOrName)
            {
            }

            public int InsertSample(SampleRecord record)
            {
                Operation(ctx => ctx.Insert(record));
                return record.Id;
            }

            public List<SampleRecord> GetAllSamples()
            {
                return Operation(ctx => ctx.Fetch<SampleRecord>());
            }
        }
    }
}
